Repository navigation
fix(*): key a running spawn's live account by its record, not its id alone - #577
Conversation
The live index of runs in flight is one per process, and a spawn was keyed into it by the node id the model chose -- unique for one conversation only. Two conversations running a spawn under the same id therefore shared an entry: the later one overwrote it, so the earlier conversation's context panel showed the other run's transcript, and the first to finish popped the entry, so the other run's live view went blank until it ended. The key is now the record's own address, the conversation's node root plus the id (spawn_live_key, the spawn side of node_live_key), which the writer holds as the record's directory and the readers as the node files' root, both resolved from the same session directory. subagent.context and tasks.list read by the same helper. Co-authored-by: Claude (claude-fable-5-1) <noreply@anthropic.com>
The gate on a running node stands on its own -- a settled node has its account on disk or none -- and the sentence about another conversation's run under the same id described the key the previous commit replaced. Co-authored-by: Claude (claude-fable-5-1) <noreply@anthropic.com>
gloryfromca
left a comment
There was a problem hiding this comment.
No blockers; this can merge as far as I am concerned.
Reviewed the full target diff and the surrounding spawn writer, live-index readers, session-path derivation, DAG keying, and the history that introduced the live overlay. The writer and both readers derive the same record-address key, while DAG activity remains in its existing namespace and persisted records are unchanged.
I also checked the repository rules in AGENTS.md / CLAUDE.md and the runtime vocabulary and architecture in CONTEXT-MAP.md / CONTEXT.md, backward compatibility, and whether the tests were weakened. Existing assertions were only adapted to the new private key shape; the added tests cover cross-conversation transcript and usage isolation, including one run finishing before the other. No UI/runtime boundary or dependency changes are involved.
Verification: uv run pytest tests/test_rpc_subagent_calls.py tests/test_rpc_tasks.py -x - 90 passed in 10.30s. git diff --check github/refactor/ui_web_architecture...HEAD also passed.
… a wait that gives up The key changed in one place and its story lived in four: the dag helper's docstring, the instance index's docstring, the glossary's live-rows entry and the settled-spawn test's rationale all still said a spawn keys its activity by a directory name or by an id unique per conversation. All four now state the record-address rule. The two-conversation test's wait for the other run to exit also fails loudly on a timeout instead of letting the assertion after it pass vacuously. Co-authored-by: Claude (claude-fable-5-1) <noreply@anthropic.com>
|
Pushed 0426674 on top of the reviewed head: no behaviour change. My own second read found the key's old story still told in four places (the |
gloryfromca
left a comment
There was a problem hiding this comment.
No blockers; this can merge as far as I am concerned.
Re-reviewed the full target diff and the delta from the previously reviewed head. The new commit is behavior-neutral: it makes the record-address description consistent across the runtime documentation and code docstrings, and the explicit loop else ensures the concurrency test cannot pass without observing the released run exit.
I rechecked the repository rules and runtime vocabulary, the affected callers and history, backward compatibility, architecture boundaries, and test strength against the unchanged functional diff; none are adversely affected by this follow-up.
Verification on 0426674058c3: uv run pytest tests/test_rpc_subagent_calls.py tests/test_rpc_tasks.py -x - 90 passed in 6.14s. The full target diff also passes git diff --check.
|
Real-gateway pass on 0426674, added to the description's Verification:
|
…alone (#577) ## Summary While a spawned sub-agent runs, its live account (transcript, usage, tool calls) is held in a process-wide index. A spawn was keyed into it by the node id the model chose, which is unique for one conversation only, so two conversations running a spawn under the same id shared one entry: the later `collecting` overwrote it, so the earlier conversation's context panel showed the other run's transcript (and, since #567, its live usage), and the first run to finish popped the entry, so the other run's live view went blank until it ended. This has been possible since a92514e (2026-09-07), when the spawn record id became the model's own `node_id`; before that the key was a minted, globally unique call id. The key is now the record's own address, the conversation's node root plus the id (`history.spawn_live_key`, the spawn side of `dag_store.node_live_key`). The writer holds the root as the record's directory and the readers as the node files' root, both resolved from the same session directory, so the two sides agree without depending on how a session key is spelled. `subagent.context` and `tasks.list` read by the same helper; the dag side already carried its run id. No wire change. ## Type - [x] Fix - [ ] Feature - [ ] Docs - [ ] CI / tooling - [ ] Refactor - [ ] Other ## Verification Run in the fix worktree, base `refactor/ui_web_architecture` at 9656dd9: - `uv run pytest tests/test_rpc_tasks.py tests/test_rpc_subagent_calls.py tests/test_subagent_activity.py`: 104 passed. Two tests are new: two conversations running a spawn under the same id each read their own usage through `tasks.list`, and each read their own transcript through `subagent.context` driven through a real `SubagentManager.spawn` with a backend that waits to be released, with the first run to finish taking nothing of the other's. The second test was run against the pre-fix source and failed there (conversation A read conversation B's transcript). - `uv run pytest tests/test_subagent_manager.py`: 159 passed. - `make lint-python` and `make test-python`: 23989 passed, 109 skipped. - Relevant Ruff check and format check: passed. - Real gateway (`raven serve` from this branch on the user's home, Raven-Code lane): two conversations dispatched a spawn under the same `node_id` at once, one saying ALPHA in two steps and one saying BRAVO in five; polled every 3 s, each conversation's `subagent.context` carried only its own word while both ran, and after the ALPHA run settled the BRAVO conversation's live view stayed up and kept growing (6 to 10 messages) instead of going blank. The #567 regression driver on the same gateway still passed its trace and subtitle checks (tool rows 0, 1, 3, 4 while running; answer landed; subtitle `Raven-Code / done / 1m02s`). - Real gateway on an isolated `RAVEN_HOME` (in-process `Raven` lane): the `tasks.list` reader, which now reads by the same key, still served the live usage -- subtitle `Raven / running / 5s / 1,881 tokens` up to `Raven / done / 49s / 8,601 tokens`, board chip 1, 2, 3 tools. ## Risk - [x] Security impact considered: this closes a cross-conversation leak of a running sub-agent's transcript and usage inside one gateway process; nothing new is exposed. - [x] Backward compatibility considered: the key is in-memory only, so no record on disk changes shape; a reader and a writer of one process always run the same code. - [x] Rollback path is clear for risky changes: revert the squash commit. ## Related Issues Fixes #580. #567 added the second reader of this index (`tasks.list`) and gated it to running nodes; the collision itself predates it. --------- Co-authored-by: Claude (claude-fable-5-1) <noreply@anthropic.com>
Summary
While a spawned sub-agent runs, its live account (transcript, usage, tool calls) is held in a process-wide index. A spawn was keyed into it by the node id the model chose, which is unique for one conversation only, so two conversations running a spawn under the same id shared one entry: the later
collectingoverwrote it, so the earlier conversation's context panel showed the other run's transcript (and, since #567, its live usage), and the first run to finish popped the entry, so the other run's live view went blank until it ended. This has been possible since a92514e (2026-09-07), when the spawn record id became the model's ownnode_id; before that the key was a minted, globally unique call id.The key is now the record's own address, the conversation's node root plus the id (
history.spawn_live_key, the spawn side ofdag_store.node_live_key). The writer holds the root as the record's directory and the readers as the node files' root, both resolved from the same session directory, so the two sides agree without depending on how a session key is spelled.subagent.contextandtasks.listread by the same helper; the dag side already carried its run id. No wire change.Type
Verification
Run in the fix worktree, base
refactor/ui_web_architectureat 9656dd9:uv run pytest tests/test_rpc_tasks.py tests/test_rpc_subagent_calls.py tests/test_subagent_activity.py: 104 passed. Two tests are new: two conversations running a spawn under the same id each read their own usage throughtasks.list, and each read their own transcript throughsubagent.contextdriven through a realSubagentManager.spawnwith a backend that waits to be released, with the first run to finish taking nothing of the other's. The second test was run against the pre-fix source and failed there (conversation A read conversation B's transcript).uv run pytest tests/test_subagent_manager.py: 159 passed.make lint-pythonandmake test-python: 23989 passed, 109 skipped.raven servefrom this branch on the user's home, Raven-Code lane): two conversations dispatched a spawn under the samenode_idat once, one saying ALPHA in two steps and one saying BRAVO in five; polled every 3 s, each conversation'ssubagent.contextcarried only its own word while both ran, and after the ALPHA run settled the BRAVO conversation's live view stayed up and kept growing (6 to 10 messages) instead of going blank. The fix(*): live spawn record, live usage and a leaner node panel subtitle #567 regression driver on the same gateway still passed its trace and subtitle checks (tool rows 0, 1, 3, 4 while running; answer landed; subtitleRaven-Code / done / 1m02s).RAVEN_HOME(in-processRavenlane): thetasks.listreader, which now reads by the same key, still served the live usage -- subtitleRaven / running / 5s / 1,881 tokensup toRaven / done / 49s / 8,601 tokens, board chip 1, 2, 3 tools.Risk
Related Issues
Fixes #580. #567 added the second reader of this index (
tasks.list) and gated it to running nodes; the collision itself predates it.